package rpc.transport;

import lombok.extern.slf4j.Slf4j;
import rpc.annotatin.Service;
import rpc.annotatin.ServiceScan;
import rpc.enums.RpcError;
import rpc.exception.RpcException;
import rpc.provider.ServiceProvider;
import rpc.registry.ServiceRegistry;
import rpc.util.ReflectUtil;

import java.net.InetSocketAddress;
import java.util.Set;

/**
 * Server抽象类 编写公共的接口实现
 *
 * @Author wupeijie
 * @Date 2020/12/4 13:58
 */
@Slf4j
public abstract class AbstractRpcServer implements RpcServer {

    protected String host;
    protected int port;

    protected ServiceRegistry serviceRegistry;
    protected ServiceProvider serviceProvider;

    /**
     * 扫描所有的服务，并注册
     */
    public void scanServices() {
        //获取启动类的完整路径
        String mainClassName = ReflectUtil.getStackTrace();
        Class<?> startClass;
        try {
            startClass = Class.forName(mainClassName);
            //判断启动类是否带有@ServiceScan注解
            if (!startClass.isAnnotationPresent(ServiceScan.class)) {
                log.error("启动类缺少 @ServiceScan 注解");
                throw new RpcException(RpcError.SERVICE_SCAN_PACKAGE_NOT_FOUND);
            }
        } catch (ClassNotFoundException e) {
            log.error("出现未知错误");
            throw new RpcException(RpcError.UNKNOWN_ERROR);
        }
        String basePackage = startClass.getAnnotation(ServiceScan.class).value();
        if ("".equals(basePackage)) {
            //若ServiceScan未声明扫描的范围，则以main所在的包作为父目录，进行递归扫描
            basePackage = mainClassName.substring(0, mainClassName.lastIndexOf("."));
        }
        //传入一个包名(启动类所在包)，用于扫描该包及其子包下所有的类
        Set<Class<?>> classSet = ReflectUtil.getClasses(basePackage);
        for (Class<?> clazz : classSet) {
            if (clazz.isAnnotationPresent(Service.class)) {
                String serviceName = clazz.getAnnotation(Service.class).name();
                Object obj;
                try {
                    //创建服务实例
                    obj = clazz.newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                    log.error("创建" + clazz + "时有错误发生");
                    continue;
                }
                if ("".equals(serviceName)) {
                    // 若无标注实现那个接口，则把所有接口都推送到注册中心
                    Class<?>[] interfaces = clazz.getInterfaces();
                    for (Class<?> oneInterface : interfaces) {
                        publishService(obj, oneInterface.getCanonicalName());
                    }
                } else {
                    // 标注实现特定的接口，则只注册一个
                    publishService(obj, serviceName);
                }
            }
        }
    }

    @Override
    public <T> void publishService(T service, String serviceName) {
        serviceProvider.addServiceProvider(service, serviceName);
        serviceRegistry.register(serviceName, new InetSocketAddress(host, port));
    }
}
